// ethersUtils.js
import { ethers } from 'ethers';
import solc from 'solc';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

// 获取当前模块路径
const __dirname = path.dirname(fileURLToPath(import.meta.url));

export default class EthersUtils {
    /**
     * 初始化区块链工具
     * @param {string} privateKey - 钱包私钥
     * @param {object} [providerConfig] - 节点配置
     */
    constructor(privateKey, providerConfig = {}) {
        // 私钥验证
        if (!ethers.isHexString(privateKey) || ethers.dataLength(privateKey) !== 32) {
            throw new Error('私钥必须是64字符的十六进制字符串');
        }

        this.provider = this._initProvider(providerConfig);
        this.wallet = new ethers.Wallet(privateKey, this.provider);
        this.contractCache = new Map();
    }

    /**
     * 初始化Provider
     * @private
     */
    _initProvider({
        apiKey = "28a6672bf55c06ec190a7fc404d8644388df631d0246fb7ed4de163d92c20114",
        network = "holesky"
    } = {}) {
        return new ethers.JsonRpcProvider(
            `https://${network}-eth.w3node.com/${apiKey}/api`,
            network
        );
    }

    /**
     * 编译Solidity合约并保存结果
     * @param {string} sourceCode - Solidity源代码
     * @param {string} [contractName] - 主合约名称
     * @param {string} [outputDir] - 编译结果输出目录
     * @returns {Promise<{abi: Array, bytecode: string, metadata: object}>}
     */
    async compileContract(sourceCode, contractName = null, outputDir = './artifacts') {
        try {
            // 定义依赖解析函数
            const findImports = (importPath) => {
                try {
                    console.log("处理导入路径:", importPath);

                    // 处理 OpenZeppelin 依赖
                    if (importPath.startsWith('@openzeppelin/')) {
                        const openzeppelinPath = importPath.replace('@openzeppelin/', '');
                        const fullPath = path.resolve(
                            process.cwd(),
                            'node_modules/@openzeppelin/',
                            openzeppelinPath
                        );
                        console.log("OpenZeppelin 路径:", fullPath);
                        return {
                            contents: fs.readFileSync(fullPath, 'utf8')
                        };
                    }
                    // 处理相对路径导入
                    else if (importPath.startsWith('./') || importPath.startsWith('../')) {
                        // 获取当前合约文件的目录
                        const currentDir = path.dirname(importPath);
                        const fullPath = path.resolve(
                            process.cwd(),
                            'assets/uploads/contracts',
                            importPath
                        );
                        console.log("相对路径导入:", {
                            importPath,
                            currentDir,
                            fullPath,
                            exists: fs.existsSync(fullPath)
                        });
                        return {
                            contents: fs.readFileSync(fullPath, 'utf8')
                        };
                    }
                    // 处理 contracts/ 开头的路径
                    else if (importPath.startsWith('contracts/')) {
                        const localPath = importPath.replace('contracts/', '');
                        const fullPath = path.resolve(
                            process.cwd(),
                            'assets/uploads/contracts',
                            localPath
                        );
                        console.log("Contracts 路径:", fullPath);
                        return {
                            contents: fs.readFileSync(fullPath, 'utf8')
                        };
                    }
                    // 处理直接文件名（同目录下的文件）
                    else {
                        const fullPath = path.resolve(
                            process.cwd(),
                            'assets/uploads/contracts',
                            importPath
                        );
                        console.log("直接文件名导入:", {
                            importPath,
                            fullPath,
                            exists: fs.existsSync(fullPath)
                        });
                        return {
                            contents: fs.readFileSync(fullPath, 'utf8')
                        };
                    }
                } catch (err) {
                    console.error(`导入错误 ${importPath}:`, err);
                    return { error: err.message };
                }
            };

            // 构造编译输入
            const input = {
                language: 'Solidity',
                sources: {
                    'contract.sol': {
                        content: sourceCode
                    }
                },
                settings: {
                    optimizer: {
                        enabled: true,
                        runs: 200
                    },
                    outputSelection: {
                        '*': {
                            '*': ["abi", "evm.bytecode"]
                        }
                    }
                }
            };

            // 执行编译
            const output = JSON.parse(solc.compile(JSON.stringify(input), { import: findImports }));

            // 错误处理
            if (output.errors) {
                const errors = output.errors
                    .filter(e => e.severity === 'error')
                    .map(e => e.formattedMessage);
                if (errors.length > 0) {
                    throw new Error(`编译错误:\n${errors.join('\n')}`);
                }
            }

            // 自动检测主合约
            const contracts = Object.keys(output.contracts['contract.sol']);
            if (!contractName) {
                contractName = contracts.find(name =>
                    output.contracts['contract.sol'][name].evm.bytecode.object !== '0x'
                ) || contracts[0];
            }

            console.log(contracts)

            // 提取编译结果
            const contract = output.contracts['contract.sol'][contractName];
            const result = {
                contractName,
                abi: contract.abi,
                bytecode: contract.evm.bytecode.object,
                timestamp: new Date().toISOString()
            };

            // 保存编译结果
            this._saveCompilationResult(result, outputDir);

            return result;
        } catch (error) {
            throw new Error(`编译失败: ${error.message}`);
        }
    }

    /**
     * 保存编译结果到文件
     * @private
     */
    _saveCompilationResult(result, outputDir) {
        try {
            // 创建输出目录
            const absolutePath = path.resolve(__dirname, outputDir);
            fs.mkdirSync(absolutePath, { recursive: true });

            // 生成文件名
            const fileName = `${result.contractName}.json`;
            const filePath = path.join(absolutePath, fileName);

            // 写入文件
            fs.writeFileSync(
                filePath,
                JSON.stringify(result, null, 2)
            );

            console.log(`编译结果已保存至: ${filePath}`);
        } catch (err) {
            console.error('保存编译结果失败:', err);
        }
    }

    /**
     * 获取合约实例
     * @param {string} address - 合约地址
     * @param {Array} abi - 合约ABI
     * @returns {ethers.Contract}
     */
    getContract(address, abi) {
        if (this.contractCache.has(address)) {
            return this.contractCache.get(address);
        }

        if (!ethers.isAddress(address)) {
            throw new Error('无效合约地址');
        }

        const contract = new ethers.Contract(address, abi, this.wallet);
        this.contractCache.set(address, contract);
        return contract;
    }

    /**
     * 部署可升级合约
     * @param {object} compileResult - 合约编译结果
     * @param {Array} constructorArgs - 构造函数参数
     * @param {object} [options] - 部署选项
     * @returns {Promise<{address: string, abi: Array, bytecode: string, logicAddress: string}>}
     */
    async deployUpgradeableContract(compileResult, constructorArgs = [], options = {}) {
        const { contractName, outputDir, storageAddress } = options;
        if (!contractName || !outputDir || !storageAddress) {
            throw new Error('Missing required parameters for upgradeable contract deployment');
        }

        try {
            // 构建完整的合约文件路径
            const contractPath = path.resolve(
                __dirname,
                outputDir,
                `${contractName}.json`
            );

            console.log('读取可升级合约文件路径:', contractPath);

            // 检查文件是否存在
            if (!fs.existsSync(contractPath)) {
                throw new Error(`可升级合约文件不存在: ${contractPath}`);
            }

            // 读取编译后的合约文件
            const contractData = JSON.parse(fs.readFileSync(contractPath, 'utf8'));
            console.log('成功读取可升级合约文件');

            // 读取存储合约的 ABI
            const storageContractPath = path.resolve(
                __dirname,
                outputDir,
                'GradeStorage.json'
            );
            console.log('读取存储合约文件路径:', storageContractPath);

            if (!fs.existsSync(storageContractPath)) {
                throw new Error(`存储合约文件不存在: ${storageContractPath}`);
            }

            const storageContractData = JSON.parse(fs.readFileSync(storageContractPath, 'utf8'));
            console.log('成功读取存储合约文件');

            // 创建合约工厂
            const factory = new ethers.ContractFactory(
                contractData.abi,
                contractData.bytecode,
                this.wallet
            );

            // 设置重试参数
            const maxRetries = 3;
            const retryDelay = 5000; // 5秒
            let lastError = null;

            for (let attempt = 1; attempt <= maxRetries; attempt++) {
                try {
                    console.log(`尝试部署可升级合约 (尝试 ${attempt}/${maxRetries})...`);

                    // 部署逻辑合约（不需要构造函数参数）
                    const logicContract = await factory.deploy({
                        gasLimit: 5000000 // 设置足够的 gas 限制
                    });

                    // 等待合约部署确认
                    const receipt = await logicContract.deploymentTransaction().wait();
                    const logicAddress = receipt.contractAddress;
                    console.log('逻辑合约部署成功:', logicAddress);

                    // 检查存储合约是否已经绑定了逻辑合约
                    const storageContract = new ethers.Contract(
                        storageAddress,
                        storageContractData.abi,
                        this.wallet
                    );

                    const existingLogicAddress = await storageContract.logicContract();
                    if (existingLogicAddress === ethers.ZeroAddress) {
                        // 初始化逻辑合约
                        console.log('初始化逻辑合约...');
                        const initializedContract = new ethers.Contract(
                            logicAddress,
                            contractData.abi,
                            this.wallet
                        );
                        await initializedContract.initialize(storageAddress);
                        console.log('逻辑合约初始化成功');

                        // 初始化存储合约
                        console.log('初始化存储合约...');
                        await storageContract.initializeLogicContract(logicAddress);
                        console.log('存储合约初始化成功');
                    } else {
                        console.log('存储合约已经绑定了逻辑合约:', existingLogicAddress);
                    }

                    return {
                        address: logicAddress,
                        abi: contractData.abi,
                        bytecode: contractData.bytecode,
                        logicAddress: logicAddress
                    };

                } catch (error) {
                    lastError = error;
                    console.error(`部署尝试 ${attempt} 失败:`, error.message);

                    if (attempt < maxRetries) {
                        console.log(`等待 ${retryDelay / 1000} 秒后重试...`);
                        await new Promise(resolve => setTimeout(resolve, retryDelay));
                    }
                }
            }

            // 如果所有重试都失败了
            throw new Error(`可升级合约部署失败: ${lastError.message}`);

        } catch (error) {
            console.error('可升级合约部署过程中发生错误:', error);
            throw error;
        }
    }

    /**
     * 部署合约
     * @param {object} compileResult - 合约编译结果
     * @param {Array} constructorArgs - 构造函数参数
     * @param {object} [options] - 部署选项
     * @returns {Promise<{address: string, abi: Array, bytecode: string}>}
     */
    async deployContract(compileResult, constructorArgs = [], options = {}) {
        const { contractName, outputDir, isUpgradeable, storageAddress } = options;
        if (!contractName || !outputDir) {
            throw new Error('Missing required parameters for contract deployment');
        }

        try {
            // 构建完整的合约文件路径
            const contractPath = path.resolve(
                __dirname,
                outputDir,
                `${contractName}.json`
            );

            console.log('读取合约文件路径:', contractPath);

            // 检查文件是否存在
            if (!fs.existsSync(contractPath)) {
                throw new Error(`合约文件不存在: ${contractPath}`);
            }

            // 读取编译后的合约文件
            const contractData = JSON.parse(fs.readFileSync(contractPath, 'utf8'));
            console.log('成功读取合约文件');

            // 创建合约工厂
            const factory = new ethers.ContractFactory(
                contractData.abi,
                contractData.bytecode,
                this.wallet
            );

            // 设置重试参数
            const maxRetries = 3;
            const retryDelay = 5000; // 5秒
            let lastError = null;

            for (let attempt = 1; attempt <= maxRetries; attempt++) {
                try {
                    console.log(`尝试部署合约 (尝试 ${attempt}/${maxRetries})...`);
                    console.log('构造函数参数:', constructorArgs);

                    let contract;
                    if (isUpgradeable) {
                        // 部署可升级合约
                        contract = await this.deployUpgradeableContract(
                            compileResult,
                            constructorArgs,
                            { ...options, storageAddress }
                        );
                    } else {
                        // 部署普通合约
                        const deployment = await factory.deploy(
                            ...constructorArgs,
                            {
                                gasLimit: 5000000 // 设置足够的 gas 限制
                            }
                        );

                        // 等待部署交易确认
                        const receipt = await deployment.deploymentTransaction().wait();
                        contract = {
                            address: receipt.contractAddress,
                            abi: contractData.abi,
                            bytecode: contractData.bytecode
                        };
                    }

                    console.log('合约部署成功:', contract.address);

                    return contract;

                } catch (error) {
                    lastError = error;
                    console.error(`部署尝试 ${attempt} 失败:`, error.message);

                    if (attempt < maxRetries) {
                        console.log(`等待 ${retryDelay / 1000} 秒后重试...`);
                        await new Promise(resolve => setTimeout(resolve, retryDelay));
                    }
                }
            }

            // 如果所有重试都失败了
            throw new Error(`合约部署失败: ${lastError.message}`);

        } catch (error) {
            console.error('合约部署过程中发生错误:', error);
            throw error;
        }
    }

    /**
     * 监听合约事件
     * @param {string} address - 合约地址
     * @param {Array} abi - 合约ABI
     * @param {string} eventName - 事件名称
     * @param {Function} callback - 回调函数
     * @returns {Function} 取消监听函数
     */
    watchContractEvent(address, abi, eventName, callback) {
        const contract = this.getContract(address, abi);
        const filter = contract.filters[eventName]();

        const listener = (...args) => {
            const event = args.pop();
            callback({
                log: event.log,
                args: event.args,
                blockNumber: event.blockNumber,
                transactionHash: event.transactionHash
            });
        };

        contract.on(filter, listener);
        return () => contract.off(filter, listener);
    }

    /**
     * 获取账户余额
     * @returns {Promise<string>} ETH余额
     */
    async getBalance() {
        const balance = await this.provider.getBalance(this.wallet.address);
        return ethers.formatEther(balance);
    }

    /**
     * 获取钱包地址
     * @returns {string}
     */
    get address() {
        return this.wallet.address;
    }

    /**
     * 生成自定义参数的Solidity兼容哈希
     * @param {Array<string>} types - 参数类型数组
     * @param {Array<any>} values - 参数值数组
     * @returns {string} 十六进制格式的哈希值
     */
    generateCustomHash(types, values) {
        if (types.length !== values.length) {
            throw new Error('参数类型和值数量不匹配');
        }
        return ethers.solidityPackedKeccak256(types, values);
    }

    /**
 * 从签名中恢复地址
 * @param {string} message - 原始消息
 * @param {string} signature - 签名
 * @returns {string} 恢复的地址
 */
    recoverAddressFromSignature(message, signature) {
        try {

            console.log('原始消息:', message);
            console.log('签名:', signature);
            const recoveredAddress = ethers.verifyMessage(message, signature);
            return recoveredAddress;


        } catch (error) {
            console.error('恢复地址失败:', error);
            throw error;
        }
    }



    /**
     * 验证签名是否有效
     * @param {string} message - 原始消息
     * @param {string} signature - 签名
     * @param {string} expectedAddress - 预期地址
     * @returns {boolean} 是否验证通过
     */
    verifySignature(message, signature, expectedAddress) {
        try {
            const recoveredAddress = this.recoverAddressFromSignature(message, signature);
            return recoveredAddress === expectedAddress;
        } catch (error) {
            console.error('验证签名失败:', error);
            return false;
        }
    }

    /**
 * 获取当前区块链网络信息
 * @returns {Promise<{chainId: number, networkName: string}>}
 */
    async getNetworkInfo() {
        try {
            const network = await this.provider.getNetwork();
            return {
                chainId: Number(network.chainId),
                networkName: network.name
            };
        } catch (error) {
            console.error('获取区块链网络信息失败:', error);
            throw error;
        }
    }

    /**
     * 获取当前链ID
     * @returns {Promise<number>}
     */
    async getChainId() {
        const networkInfo = await this.getNetworkInfo();
        return networkInfo.chainId;
    }

    /**
     * 为消息添加以太坊前缀并生成哈希
     * @param {string} message - 原始消息
     * @returns {string} 带前缀的消息哈希
     */
    hashMessage(message) {
        try {
            // 使用 ethers.js 的 hashMessage 方法，它会自动添加以太坊消息前缀
            return ethers.hashMessage(message);
        } catch (error) {
            console.error('生成消息哈希失败:', error);
            throw error;
        }
    }

    /**
     * 获取 ethers 对象
     *
     * @returns 返回 ethers 对象
     */
    getEthers(){
        return ethers;
    }
}


